import re
import allure

from jsonpath import jsonpath
from hamcrest import *

from utils.global_data import global_data
from utils.logger import log


class ValidatableResponse():

    def __init__(self, response):
        self.__response = response

    def body(self, expected_value, func=contains_string):

        actual_value = self.__response.text
        with allure.step("Check body with {}".format(str(func.__name__))):
            self.__print_step(expected_value, actual_value)
            assert_that(actual_value, func(expected_value))
        return self

    def body_regular(self, regex, expected_value, func=contains_string, position=1):

        response_message = self.__response.text
        actual_value = re.search(regex, response_message).group(position)
        with allure.step("Check regular body with {}".format(str(func.__name__))):
            self.__print_step(expected_value, actual_value)
            assert_that(actual_value, func(expected_value))
        return self

    def status_code(self, expected_value):

        actual_value = self.__response.status_code

        with allure.step("Check status_code"):
            self.__print_step(expected_value, actual_value)
            assert_that(str(actual_value), equal_to(str(expected_value)))
        return self

    def header(self, key, expected_value, func=contains_string):

        try:
            actual_value = self.__response.headers[key]
            with allure.step("Check header [{}] with {}".format(key, str(func.__name__))):

                self.__print_step(expected_value, actual_value)
                assert_that(actual_value, func(expected_value))
        except KeyError:
            log.error("Actual Header is,key:{} doesn't exist".format(key))
            raise
        return self

    def headers(self, actual_headers, expected_headers):

        try:
            with allure.step("Check headers [{}]".format(actual_headers)):

                self.__print_step(expected_headers, actual_headers)
                assert_that(actual_headers, has_entries(expected_headers))
        except KeyError:
            log.error("Assert headers filed, actual headers is: ".format(actual_headers))
            raise
        return self

    def cookie(self, key, expected_value, func=contains_string):

        try:
            actual_value = self.__response.cookies.get(key)
            with allure.step("Check cookie [{}] with {}".format(key, str(func.__name__))):

                self.__print_step(expected_value, actual_value)
                assert_that(actual_value, func(expected_value))
        except KeyError:
            log.error("Actual Cookie is,key:{} doesn't exist".format(key))
            raise
        return self

    def cookies(self, actual_cookies, expected_cookies):

        try:
            with allure.step("Check cookie [{}]".format(actual_cookies)):

                self.__print_step(expected_cookies, actual_cookies)
                assert_that(actual_cookies, has_entries(expected_cookies))
        except KeyError:
            log.error("Assert cookies filed, actual cookies is: ".format(actual_cookies))
            raise
        return self

    def json_path(self, json_path, expected_value, func=equal_to, position=0):
        global actual_value
        actual_value = jsonpath(self.__response.json(), json_path)
        if type(actual_value) is not list:
            log.error(
                "Assert json filed,Please check JSONPath:{}".format(json_path))
            assert False

        with allure.step("Check JSON [{}] with {}".format(json_path, str(func.__name__))):
            if expected_value is not None and ValidatableResponse.__is_valid_regex(expected_value):
                self.__print_step("regex is {}".format(expected_value), actual_value)
                assert re.match(expected_value, actual_value[position])
            else:
                self.__print_step(expected_value, actual_value)
                assert_that(actual_value[position], func(expected_value))
        return self

    def extract(self):
        return ExtractableResponse(self.__response)

    def response(self):
        return self.__response

    def __print_step(self, expected_value, actual_value):

        allure.attach(name="Expected result", body=str(expected_value))
        allure.attach(name="Actual result", body=str(actual_value))
        log.info("Expected result:{},Actual result:{}".format(str(expected_value),

                                                              str(actual_value).replace('\r', '').replace('\n', '')))

    @staticmethod
    def __is_valid_regex(regex):
        if regex is None:
            return False
        try:
            re.compile(regex)
            return True
        except TypeError:
            return False


class ExtractableResponse():

    def __init__(self, response):
        self.__response = response
        self.__step_var = {}

    def status_code(self, extract_var=None, is_global=False):
        extract_value = self.__response.status_code
        self.__step_var[extract_var] = extract_value

        if is_global:
            global_data.set_data(extract_var, extract_value)
        log.info("Extract status_code,variable name is:{},value is:{}".format(extract_var, extract_value))
        return self

    def header(self, key, extract_var=None, is_global=False):

        extract_value = self.__response.headers[key]
        self.__step_var[extract_var] = extract_value

        if is_global:
            global_data.set_data(extract_var, extract_value)
        log.info("Extract header,variable name is:{},value is:{}".format(extract_var, extract_value))
        return self

    def cookie(self, key, extract_var=None, is_global=False):

        extract_value = self.__response.cookies.get(key)
        self.__step_var[extract_var] = extract_value

        if is_global:
            global_data.set_data(extract_var, extract_value)
        log.info("Extract cookie,variable name is:{},value is:{}".format(extract_var, extract_value))
        return self

    def body(self, regex, extract_var=None, position=1, is_global=False):

        response_message = self.__response.text
        extract_value = re.search(regex, response_message).group(position)
        self.__step_var[extract_var] = extract_value

        if is_global:
            global_data.set_data(extract_var, extract_value)
        log.info("Extract body,variable name is:{},value is:{}".format(extract_var, extract_value))
        return self

    def json_path(self, json_path, extract_var=None, position=0, is_global=False):

        global extract_value
        try:
            extract_value = jsonpath(self.__response.json(), json_path)[position]
        except Exception as e:
            log.error("Extract json data failed,please check JSONPath:{}, exception is {}".format(json_path, e))
        self.__step_var[extract_var] = extract_value

        if is_global:
            global_data.set_data(extract_var, extract_value)
        log.info("Extract json data,variable name is:{},value is:{}".format(extract_var, extract_value))
        return self

    def response(self):
        return self.__response

    def step_values(self):
        return self.__step_var
